package core

import (
	"fmt"
	hp "go-sniffer/plugSrc/http/build"
	mongodb "go-sniffer/plugSrc/mongodb/build"
	mysql "go-sniffer/plugSrc/mysql/build"
	redis "go-sniffer/plugSrc/redis/build"
	"io"
	"path/filepath"
	"plugin"

	gopacket "github.com/google/gopacket"
)

type Plug struct {
	dir           string                                                        // 外部插件目录路径
	ResolveStream func(net gopacket.Flow, transport gopacket.Flow, r io.Reader) // 当前选中的解析函数
	BPF           string                                                        // 当前选中的BPF过滤规则

	InternalPlugList map[string]PlugInterface // 内置插件列表
}

// All internal plug-ins must implement this interface
// ResolvePacket - entry
// BPFFilter     - set BPF, like: mysql(tcp and port 3306)
// SetFlag       - plug-in params
// Version       - plug-in version
type PlugInterface interface {
	ResolveStream(net gopacket.Flow, transport gopacket.Flow, r io.Reader) // 解析数据流
	BPFFilter() string                                                     // 返回BPF过滤规则
	SetFlag([]string)                                                      // 设置插件参数
	Version() string                                                       // 返回版本信息
}

func NewPlug() *Plug {
	var p Plug
	p.dir, _ = filepath.Abs("./plug/") // 设置默认插件目录
	p.LoadInternalPlugList()           // 加载内置插件
	return &p
}

// 加载内部插件
// LoadInternalPlugList函数实际上只是给cmd.printPlugList使用
func (p *Plug) LoadInternalPlugList() {
	list := make(map[string]PlugInterface)

	// 注册所有内置插件
	list["mysql"] = mysql.NewInstance()     // MySQL协议解析
	list["mongodb"] = mongodb.NewInstance() // MongoDB协议解析
	list["redis"] = redis.NewInstance()     // Redis协议解析
	list["http"] = hp.NewInstance()         // HTTP协议解析

	p.InternalPlugList = list
}

func (p *Plug) ChangePath(dir string) {
	p.dir = dir
}

func (p *Plug) PrintList() {
	//Print Internal Plug
	for inPlugName := range p.InternalPlugList {
		fmt.Println("internal plug : " + inPlugName)
	}

	//split
	fmt.Println("-- --- --")
}

// SetOption是真正加载插件的位置
// cmd.parsePlugCmd  → cmd.plugHandle.SetOption →  加载到type Plug struct核心内容
// 用户选择插件 → 重新打开.so文件 → 获取函数 → 类型断言 → 设置当前插件
func (p *Plug) SetOption(plugName string, plugParams []string) {
	//Load Internal Plug
	if internalPlug, ok := p.InternalPlugList[plugName]; ok {
		p.ResolveStream = internalPlug.ResolveStream // 设置解析函数
		internalPlug.SetFlag(plugParams)             // 配置插件参数
		p.BPF = internalPlug.BPFFilter()             // 获取BPF规则
		return
	}

	// 如果不是内置插件，尝试动态加载外部插件
	plug, err := plugin.Open("./plug/" + plugName)
	if err != nil {
		panic(err)
	}

	// 从外部插件获取函数
	resolvePacket, err := plug.Lookup("ResolvePacket")
	if err != nil {
		panic(err)
	}
	setFlag, err := plug.Lookup("SetFlag")
	if err != nil {
		panic(err)
	}
	BPFFilter, err := plug.Lookup("BPFFilter")
	if err != nil {
		panic(err)
	}

	// 设置当前使用的插件函数
	p.ResolveStream = resolvePacket.(func(net gopacket.Flow, transport gopacket.Flow, r io.Reader))
	setFlag.(func([]string))(plugParams)
	p.BPF = BPFFilter.(func() string)()
}
